bitkeeper revision 1.1159.3.1 (4113ca544f2ijHD3gffCMUC9u9wOaQ)
authorcl349@freefall.cl.cam.ac.uk <cl349@freefall.cl.cam.ac.uk>
Fri, 6 Aug 2004 18:13:40 +0000 (18:13 +0000)
committercl349@freefall.cl.cam.ac.uk <cl349@freefall.cl.cam.ac.uk>
Fri, 6 Aug 2004 18:13:40 +0000 (18:13 +0000)
Make time code more robust.

linux-2.6.7-xen-sparse/arch/xen/i386/kernel/time.c
linux-2.6.7-xen-sparse/arch/xen/i386/kernel/timers/timer_tsc.c
linux-2.6.7-xen-sparse/include/asm-xen/asm-i386/mach-xen/do_timer.h
linux-2.6.7-xen-sparse/include/asm-xen/asm-i386/ptrace.h

index 82a8a91d8164887dbde1320b17745c6673faaea8..c595ce1183e4577743e578c97d7cf002c8d14129 100644 (file)
@@ -87,15 +87,71 @@ EXPORT_SYMBOL(i8253_lock);
 
 struct timer_opts *cur_timer = &timer_none;
 
-extern u64 shadow_system_time;
-extern u32 shadow_time_delta_usecs;
-extern void __get_time_values_from_xen(void);
+/* These are peridically updated in shared_info, and then copied here. */
+u32 shadow_tsc_stamp;
+u64 shadow_system_time;
+static u32 shadow_time_version;
+static struct timeval shadow_tv;
+extern u64 processed_system_time;
+
+#define NS_PER_TICK (1000000000ULL/HZ)
+
+/*
+ * Reads a consistent set of time-base values from Xen, into a shadow data
+ * area. Must be called with the xtime_lock held for writing.
+ */
+int __get_time_values_from_xen(void)
+{
+       s64 delta;
+       unsigned int ticks = 0;
+
+       do {
+               shadow_time_version = HYPERVISOR_shared_info->time_version2;
+               rmb();
+               shadow_tv.tv_sec    = HYPERVISOR_shared_info->wc_sec;
+               shadow_tv.tv_usec   = HYPERVISOR_shared_info->wc_usec;
+               shadow_tsc_stamp    = HYPERVISOR_shared_info->tsc_timestamp.tsc_bits;
+               shadow_system_time  = HYPERVISOR_shared_info->system_time;
+               rmb();
+       }
+       while (shadow_time_version != HYPERVISOR_shared_info->time_version1);
+
+       delta = (s64)(shadow_system_time +
+                     cur_timer->get_offset() * NSEC_PER_USEC -
+                     processed_system_time);
+       if (delta < 0) {
+               printk("Timer ISR: Time went backwards: %lld\n", delta);
+               return 1;
+       }
+
+       if (delta < NS_PER_TICK)
+               return 1;
+
+       /* Process elapsed jiffies since last call. */
+       while (delta >= NS_PER_TICK) {
+               ticks++;
+               delta -= NS_PER_TICK;
+               processed_system_time += NS_PER_TICK;
+       }
+       jiffies_64 += ticks - 1;
+       /* We leave one tick for the caller to add to jiffies since
+        * the timer interrupt will call do_timer(). */
+
+       return 0;
+}
+
+#define TIME_VALUES_UP_TO_DATE \
+       (shadow_time_version == HYPERVISOR_shared_info->time_version2)
+
+/*
+ * We use this to ensure that gettimeofday() is monotonically increasing. We
+ * only break this guarantee if the wall clock jumps backwards "a long way".
+ */
+static struct timeval last_seen_tv = {0,0};
 
 /* Keep track of last time we did processing/updating of jiffies and xtime. */
 u64 processed_system_time;   /* System time (ns) at last processing. */
 
-#define NS_PER_TICK (1000000000ULL/HZ)
-
 /*
  * This version of gettimeofday has microsecond resolution
  * and better than microsecond precision on fast x86 machines with TSC.
@@ -130,7 +186,22 @@ void do_gettimeofday(struct timeval *tv)
                        usec += lost * (USEC_PER_SEC / HZ);
 
                sec = xtime.tv_sec;
-               usec += (xtime.tv_nsec / 1000);
+               usec += (xtime.tv_nsec / NSEC_PER_USEC);
+
+               if (unlikely(!TIME_VALUES_UP_TO_DATE)) {
+                       /*
+                        * We may have blocked for a long time,
+                        * rendering our calculations invalid
+                        * (e.g. the time delta may have
+                        * overflowed). Detect that and recalculate
+                        * with fresh values.
+                        */
+                       write_seqlock(&xtime_lock);
+                       if (__get_time_values_from_xen() == 0)
+                               jiffies_64++;
+                       write_sequnlock(&xtime_lock);
+                       continue;
+               }
        } while (read_seqretry(&xtime_lock, seq));
 
        while (usec >= 1000000) {
@@ -138,6 +209,16 @@ void do_gettimeofday(struct timeval *tv)
                sec++;
        }
 
+       /* Ensure that time-of-day is monotonically increasing. */
+       if ((sec < last_seen_tv.tv_sec) ||
+           ((sec == last_seen_tv.tv_sec) && (usec < last_seen_tv.tv_usec))) {
+               sec = last_seen_tv.tv_sec;
+               usec = last_seen_tv.tv_usec;
+       } else {
+               last_seen_tv.tv_sec = sec;
+               last_seen_tv.tv_usec = usec;
+       }
+
        tv->tv_sec = sec;
        tv->tv_usec = usec;
 }
@@ -172,6 +253,9 @@ int do_settimeofday(struct timespec *tv)
        time_status |= STA_UNSYNC;
        time_maxerror = NTP_PHASE_LIMIT;
        time_esterror = NTP_PHASE_LIMIT;
+
+       last_seen_tv.tv_sec = 0;
+
        write_sequnlock_irq(&xtime_lock);
        clock_was_set();
        return 0;
@@ -236,8 +320,7 @@ static inline void do_timer_interrupt(int irq, void *dev_id,
        }
 #endif
 
-       if (regs)
-               do_timer_interrupt_hook(regs);
+       do_timer_interrupt_hook(regs);
 
 #if 0                          /* XEN PRIV */
        /*
@@ -288,8 +371,6 @@ static inline void do_timer_interrupt(int irq, void *dev_id,
  */
 irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-       s64 delta;
-
        /*
         * Here we are in the timer irq handler. We just have irqs locally
         * disabled but we don't know if the timer_bh is running on the other
@@ -299,17 +380,7 @@ irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
         */
        write_seqlock(&xtime_lock);
 
-       __get_time_values_from_xen();
-
-       shadow_time_delta_usecs = cur_timer->get_offset() * NSEC_PER_USEC;
-       delta = (s64)(shadow_system_time + shadow_time_delta_usecs -
-                     processed_system_time);
-       if (delta < 0) {
-               printk("Timer ISR: Time went backwards: %lld\n", delta);
-               goto out;
-       }
-
-       if (delta < NS_PER_TICK)
+       if (__get_time_values_from_xen())
                goto out;
 
        cur_timer->mark_offset();
@@ -426,12 +497,15 @@ void __init time_init(void)
 #endif
        xtime.tv_sec = HYPERVISOR_shared_info->wc_sec;
        wall_to_monotonic.tv_sec = -xtime.tv_sec;
-       xtime.tv_nsec = HYPERVISOR_shared_info->wc_usec * 1000;
+       xtime.tv_nsec = HYPERVISOR_shared_info->wc_usec * NSEC_PER_USEC;
        wall_to_monotonic.tv_nsec = -xtime.tv_nsec;
 
        cur_timer = select_timer();
        printk(KERN_INFO "Using %s for high-res timesource\n",cur_timer->name);
 
+       __get_time_values_from_xen();
+       processed_system_time = shadow_system_time;
+
        time_irq  = bind_virq_to_irq(VIRQ_TIMER);
 
        (void)setup_irq(time_irq, &irq_timer);
index 623b85c9dee7387dbf692fd206a74caaf9eb5a8b..87bc7f32b5b849d1cd50bb2e92cbdf155c0b7c55 100644 (file)
@@ -79,39 +79,9 @@ static int count2; /* counter for mark_offset_tsc() */
  */
 static unsigned long fast_gettimeoffset_quotient;
 
-
-/* These are peridically updated in shared_info, and then copied here. */
-static u32 shadow_tsc_stamp;
-u64 shadow_system_time;
-static u32 shadow_time_version;
-static struct timeval shadow_tv;
-u32 shadow_time_delta_usecs;
-static unsigned int rdtsc_bitshift;
-extern u64 processed_system_time;
-
-#define NS_PER_TICK (1000000000ULL/HZ)
-
-/*
- * Reads a consistent set of time-base values from Xen, into a shadow data
- * area. Must be called with the xtime_lock held for writing.
- */
-void __get_time_values_from_xen(void)
-{
-       do {
-               shadow_time_version = HYPERVISOR_shared_info->time_version2;
-               rmb();
-               shadow_tv.tv_sec    = HYPERVISOR_shared_info->wc_sec;
-               shadow_tv.tv_usec   = HYPERVISOR_shared_info->wc_usec;
-               shadow_tsc_stamp    = HYPERVISOR_shared_info->tsc_timestamp.tsc_bits;
-               shadow_system_time  = HYPERVISOR_shared_info->system_time;
-               rmb();
-       }
-       while (shadow_time_version != HYPERVISOR_shared_info->time_version1);
-}
-
-#define TIME_VALUES_UP_TO_DATE \
-       (shadow_time_version == HYPERVISOR_shared_info->time_version2)
-
+unsigned int rdtsc_bitshift;
+extern u32 shadow_tsc_stamp;
+extern u64 shadow_system_time;
 
 static unsigned long get_offset_tsc(void)
 {
@@ -188,23 +158,9 @@ unsigned long long sched_clock(void)
 
 static void mark_offset_tsc(void)
 {
-       s64 delta;
-       unsigned int ticks = 0;
-
-       write_seqlock(&monotonic_lock);
-
-       delta = (s64)(shadow_system_time + shadow_time_delta_usecs -
-                     processed_system_time);
-
-       /* Process elapsed jiffies since last call. */
-       while (delta >= NS_PER_TICK) {
-               ticks++;
-               delta -= NS_PER_TICK;
-               processed_system_time += NS_PER_TICK;
-       }
-       jiffies_64 += ticks - 1;
 
        /* update the monotonic base value */
+       write_seqlock(&monotonic_lock);
        monotonic_base = shadow_system_time;
        monotonic_offset = shadow_tsc_stamp;
        write_sequnlock(&monotonic_lock);
@@ -383,7 +339,8 @@ static int __init init_tsc(char* override)
        printk(KERN_INFO "Xen reported: %lu.%03lu MHz processor.\n", 
               cpu_khz / 1000, cpu_khz % 1000);
 
-       /* (10^6 * 2^32) / cpu_khz = (2^32 * 1 / (clocks/us)) */
+       /* (10^6 * 2^32) / cpu_hz = (10^3 * 2^32) / cpu_khz =
+          (2^32 * 1 / (clocks/us)) */
        {       
                unsigned long eax=0, edx=1000;
                __asm__("divl %2"
@@ -396,9 +353,6 @@ static int __init init_tsc(char* override)
 
        set_cyc2ns_scale(cpu_khz/1000);
 
-       __get_time_values_from_xen();
-       processed_system_time = shadow_system_time;
-
        rdtscll(alarm);
 
        return 0;
index 8ac79726f5d13627ccace97a6f93a1374a5b3f15..565ba968cde44d3c6ca7566f04d0df231a040ce9 100644 (file)
@@ -22,9 +22,10 @@ static inline void do_timer_interrupt_hook(struct pt_regs *regs)
  * system, in that case we have to call the local interrupt handler.
  */
 #ifndef CONFIG_X86_LOCAL_APIC
-       x86_do_profile(regs);
+       if (regs)
+               x86_do_profile(regs);
 #else
-       if (!using_apic_timer)
+       if (regs && !using_apic_timer)
                smp_local_timer_interrupt(regs);
 #endif
 }
index 0165ccc5429e3c2a6e9e43ce98af75f57af7dec0..eabe6d32678d4c29f4cf5aadf095f9775d1a2d17 100644 (file)
@@ -55,7 +55,7 @@ struct pt_regs {
 #define PTRACE_SET_THREAD_AREA    26
 
 #ifdef __KERNEL__
-#define user_mode(regs) ((VM_MASK & (regs)->eflags) || (2 & (regs)->xcs))
+#define user_mode(regs) ((regs) && ((VM_MASK & (regs)->eflags) || (2 & (regs)->xcs)))
 #define instruction_pointer(regs) ((regs)->eip)
 #endif